package com.qfedu.mbti.service.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qfedu.mbti.config.RedisKeyConfig;
import com.qfedu.mbti.dao.UserDao;
import com.qfedu.mbti.dao.UserLogDao;
import com.qfedu.mbti.entity.User;
import com.qfedu.mbti.entity.UserLog;
import com.qfedu.mbti.param.UserAdd;
import com.qfedu.mbti.param.UserFind;
import com.qfedu.mbti.param.UserLogin;
import com.qfedu.mbti.param.UserLoginCode;
import com.qfedu.mbti.service.UserService;
import com.qfedu.mbti.sms.AliSmsUtil;
import com.qfedu.mbti.util.TokenUtil;
import com.qfedu.mbti.vo.R;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 用户表(User)表服务实现类
 *
 * @author makejava
 * @since 2024-11-04 11:43:41
 */
@Service("userService")
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
    private final UserDao dao;
    private final UserLogDao logDao;
    private final StringRedisTemplate stringRedisTemplate;

    @Transactional //事务 保证多个sql的操作一致性
    @Override
    public R<String> register(UserAdd add) {
        //1.唯一性校验
        User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone,add.getPhone()));
        if(user!=null){
            return R.fail("亲，手机号已存在！");
        }
        //2.组装对象 实现数据库新增
        //密码 密文 加密处理 混合加密
        String pass= SecureUtil.sha1(SecureUtil.md5(add.getPassword()));

        user=new User(add.getNickName(),add.getPhone(), pass);
        if(dao.insert(user)>0){
            //3.记录日志
            logDao.insert(new UserLog(user.getId(),"注册","实现了新用户注册，奖励10次考试机会"));
            return R.ok(null);
        }

        return R.fail("亲，注册失败！");
    }

    @Override
    public R login(UserLogin login) {
        //1.查询 密码 密文
        LambdaQueryWrapper<User> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone,login.getPhone());
        wrapper.eq(User::getPassword,SecureUtil.sha1(SecureUtil.md5(login.getPassword())));
        User user=dao.selectOne(wrapper);

        //2.校验是否登录成功
        if(user!=null && user.getStatus().equals("有效")){

            //3.生成令牌 令牌(Token)凭证，登录成功的 一般令牌的生成算法：JWT(三部分)
            String token= TokenUtil.createToken(user.getId()+"");
            //4.存储令牌 Redis
            //使用Redis灵魂五问：1.为什么使用？2.使用什么数据类型？3.存储什么数据 4.有没有有效期 5.要不要考虑数据同步
            stringRedisTemplate.opsForValue().set(RedisKeyConfig.USER_TOKEN+token,user.getId()+"",2,TimeUnit.HOURS);
            //记录日志
            logDao.insert(new UserLog(user.getId(),"密码登录","通过手机号密码登录，成功！"));
            //5.返回信息
            Map<String,Object> result=new HashMap<>();
            result.put("token",token);
            user.setPassword("");
            result.put("user",user);
            return R.ok(result);
        }

        return R.fail("亲，账号或密码失败！");
    }

    @Override
    public R updatePass(String password, String token) {
        //1.获取
        String uid=stringRedisTemplate.opsForValue().get(RedisKeyConfig.USER_TOKEN+token);
        //2.密码 密文
        String pass=SecureUtil.sha1(SecureUtil.md5(password));
        User user=new User();
        user.setId(Integer.parseInt(uid));
        user.setPassword(pass);
        user.setUpdateTime(new Date());

        if(dao.updateById(user)>0){
            //修改成功 记录日志
            logDao.insert(new UserLog(user.getId(),"修改密码","实现了密码的修改！"));
            //删除令牌
            stringRedisTemplate.delete(RedisKeyConfig.USER_TOKEN+token);
            return R.ok(null);
        }
        return R.fail("亲，密码修改失败！");
    }

    @Override
    public R queryAll() {
        return R.ok(dao.selectList(null));
    }

    @Override
    public R sendLcode(String phone) {
        //1.创建验证码
        String code= RandomUtil.randomNumbers(6);
        //2.发送验证吗
//        if(AliSmsUtil.sendCode(AliSmsUtil.SMS_TEM_LCODE,phone,code)){
          if(true){
            //3.发送成功 存储验证吗 使用Redis
            stringRedisTemplate.opsForValue().set(RedisKeyConfig.SMS_LCODE+phone,code,RedisKeyConfig.SMS_CODE_TIME, TimeUnit.SECONDS);
            //4.记录日志
            User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone,phone));
            Integer uid=0;
            if(user!=null){
                uid=user.getId();
            }
            logDao.insert(new UserLog(uid,"登录验证码","实现登录验证码的发送"));
//            return R.ok(null);
            return R.ok(code);
        }
        return R.fail("亲，验证码发送失败！");
    }

    @Override
    public R loginCode(UserLoginCode code) {
        //如果当前手机号不是账号 就自动注册，如果是正常登录
        //1.获取验证码
        if(stringRedisTemplate.hasKey(RedisKeyConfig.SMS_LCODE+code.getPhone())){
            //有验证码，比较是否正确
            if(Objects.equals(code.getCode(),stringRedisTemplate.opsForValue().get(RedisKeyConfig.SMS_LCODE+code.getPhone()))){
                //本次验证码登录 成功
                //3.验证是否已注册，如果未注册 自动注册
                User user=dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone,code.getPhone()));
                if(user==null){
                    //自动注册
                    user=new User(RandomUtil.randomString(6),code.getPhone(),
                            SecureUtil.sha1(SecureUtil.md5(code.getPhone().substring(code.getPhone().length()-6))));
                    dao.insert(user);
                    //记录日志
                    logDao.insert(new UserLog(user.getId(),"自动注册","手机号验证码首次登录，自动注册账号"));
                }
                //4.生成令牌
                String token=TokenUtil.createToken(user.getId()+"");

                //5.存储令牌 Redis
                //使用Redis灵魂五问：1.为什么使用？2.使用什么数据类型？3.存储什么数据 4.有没有有效期 5.要不要考虑数据同步
                stringRedisTemplate.opsForValue().set(RedisKeyConfig.USER_TOKEN+token,user.getId()+"",2,TimeUnit.HOURS);

                //记录日志
                logDao.insert(new UserLog(user.getId(),"验证码登录","通过手机号验证码登录，成功！"));
                //删除验证码
                stringRedisTemplate.delete(RedisKeyConfig.SMS_LCODE+code.getPhone());

                //6.返回信息
                Map<String,Object> result=new HashMap<>();
                result.put("token",token);
                user.setPassword("");
                result.put("user",user);

                return R.ok(result);
            }
        }
        return R.fail("亲，手机号或验证码错误！");
    }

    @Override
    public R sendFcode(String phone) {
        LambdaQueryWrapper<User> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(User::getPhone,phone);
        User user=dao.selectOne(wrapper);
        if(user!=null) {
            //1.创建验证码
            String code = RandomUtil.randomNumbers(6);
            //2.发送验证吗
//        if(AliSmsUtil.sendCode(AliSmsUtil.SMS_TEM_LCODE,phone,code)){
            if (true) {
                //3.发送成功 存储验证吗 使用Redis
                stringRedisTemplate.opsForValue().set(RedisKeyConfig.SMS_FCODE + phone, code, RedisKeyConfig.SMS_CODE_TIME, TimeUnit.SECONDS);
                //4.记录日志
                logDao.insert(new UserLog(user.getId(), "找回验证码", "实现密码找回验证码的发送"));
//            return R.ok(null);
                return R.ok(code);
            }
            return R.fail("亲，验证码发送失败！");
        }else {
            return R.fail("亲，你的手机号没有注册呀！");
        }

    }

    @Override
    public R findPass(UserFind find) {
        //1.比较验证码
        String key=RedisKeyConfig.SMS_FCODE+find.getPhone();
        if(stringRedisTemplate.hasKey(key)) {
            if(stringRedisTemplate.opsForValue().get(key).equals(find.getCode())) {
                //2.获取
                User user = dao.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, find.getPhone()));

                //3.密码 密文
                String pass = SecureUtil.sha1(SecureUtil.md5(find.getPassword()));
                user.setPassword(pass);
                user.setUpdateTime(new Date());

                if (dao.updateById(user) > 0) {
                    //修改成功 记录日志
                    logDao.insert(new UserLog(user.getId(), "密码找回", "完成了密码找回操作，密码已修改"));
                    stringRedisTemplate.delete(key);

                    return R.ok(null);
                }
            }
        }
        return R.fail("亲，请检查验证码！");
    }
}

